﻿#region << 版 本 注 释 >>

/*----------------------------------------------------------------
 * 版权所有 (c) 2022 北京超维景生物科技有限公司 保留所有权利。
 * 
 * 创建者：huangyang
 * 电子邮箱：huangyang@tvscope.cn
 * 创建时间：2023/2/20 15:42:13
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/

#endregion << 版 本 注 释 >>

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ImageK.Java;
using ImageK.Plugin.Frame;
using ImageK.Plugin.Tool;
using ImageK.Util;
using Timer = System.Windows.Forms.Timer;

namespace ImageK.Gui
{
    public class ToolBar : ToolStrip
    {
        public const int RECTANGLE = 0;
        public const int OVAL = 1;
        public const int POLYGON = 2;
        public const int FREEROI = 3;
        public const int LINE = 4;
        public const int POLYLINE = 5;
        public const int FREELINE = 6;
        public const int POINT = 7, CROSSHAIR = 7;
        public const int WAND = 8;
        public const int TEXT = 9;
        public const int UNUSED = 10;
        public const int MAGNIFIER = 11;
        public const int HAND = 12;
        public const int DROPPER = 13;
        public const int ANGLE = 14;
        public const int CUSTOM1 = 15;
        public const int CUSTOM2 = 16;
        public const int CUSTOM3 = 17;
        public const int CUSTOM4 = 18;
        public const int CUSTOM5 = 19;
        public const int CUSTOM6 = 20;
        public const int CUSTOM7 = 21;

        public const int DOUBLE_CLICK_THRESHOLD = 650; //ms

        public const int RECT_ROI = 0, ROUNDED_RECT_ROI = 1, ROTATED_RECT_ROI = 2;
        public const int OVAL_ROI = 0, ELLIPSE_ROI = 1, BRUSH_ROI = 2;

        public string[] builtInTools = new string[]
        {
            "Arrow", "Brush", "Command Finder", "Developer Menu", "Flood Filler",
            "Label Maker", "LUT Menu", "Overlay Brush", "Pencil", "Pixel Inspector", "Selection Rotator",
            "Spray Can", "Stacks Menu", "ROI Menu"
        };

        private string[] builtInTools2 = new string[] { "Pixel Inspection Tool", "Paintbrush Tool", "Flood Fill Tool" };

        private const int NUM_TOOLS = 23;
        private const int MAX_EXTRA_TOOLS = 8;
        private const int MAX_TOOLS = 31; //NUM_TOOLS + MAX_EXTRA_TOOLS
        private const int NUM_BUTTONS = 21;
        private const int BUTTON_WIDTH = 30;
        private const int BUTTON_HEIGHT = 31;
        private const int SIZE = 28; // no longer used
        private const int GAP_SIZE = 9;
        private const int OFFSET = 7;
        private const string BRUSH_SIZE = "toolbar.brush.size";
        public const string CORNER_DIAMETER = "toolbar.arc.size";
        public static string TOOL_KEY = "toolbar.tool";

        private Size ps;
        private bool[] down;
        private static int current;
        private int previousTool;
        private int x, y;
        private int xOffset, yOffset;
        private long mouseDownTime;
        private Graphics _g;
        private Pen _pen;
        private Brush _brush;
        private static ToolBar instance;
        private int mpPrevious = RECTANGLE;
        private string[] names = new string[MAX_TOOLS];

        private string[] icons = new string[MAX_TOOLS];

        private PlugInTool[] tools = new PlugInTool[MAX_TOOLS];
        private ToolStripDropDownButton[] menus = new ToolStripDropDownButton[MAX_TOOLS];
        private int nExtraTools;

        // private MacroInstaller macroInstaller;
        private bool addingSingleTool;
        private bool installingStartupTool;
        private bool doNotSavePrefs;
        private int pc;
        private string icon;
        private int startupTime;

        private ToolStripDropDownButton
            rectPopup, ovalPopup, pointPopup, linePopup, zoomPopup, pickerPopup, switchPopup;

        // private CheckboxMenuItem rectItem, roundRectItem, rotatedRectItem;
        // private CheckboxMenuItem ovalItem, ellipseItem, brushItem;
        // private CheckboxMenuItem pointItem, multiPointItem;
        // private CheckboxMenuItem straightLineItem, polyLineItem, freeLineItem, arrowItem;
        private string currentSet = "Startup Macros";
        private Timer pressTimer;
        private static int longClickDelay = 600; //ms
        private bool disableRecording;

        private static Color foregroundColor = Color.White; //Prefs.getColor(Prefs.FCOLOR, Color.white);
        private static Color backgroundColor = Color.Black; //Prefs.getColor(Prefs.BCOLOR, Color.black);
        private static double foregroundValue = Double.NaN;
        private static double backgroundValue = Double.NaN;
        private static int ovalType = OVAL_ROI;
        private static int rectType = RECT_ROI;
        private static bool multiPointMode = Prefs.multiPointMode;
        private static bool arrowMode;
        private static int brushSize = 15; //(int)Prefs.get(BRUSH_SIZE, 15);
        private static int arcSize = 20; //(int)Prefs.get(CORNER_DIAMETER, 20);
        private int lineType = LINE;
        private static bool legacyMode;
        private static double dscale = 1.0;
        private static int scale = 1;
        private static int buttonWidth;
        private static int buttonHeight;
        private static int gapSize;
        private static int offset;

        private static Color gray = Color.FromArgb(228, 228, 228);
        private static Color brighter = gray.Brighter(); //gray.brighter();
        private static Color darker = Color.FromArgb(180, 180, 180);
        private static Color evenDarker = Color.FromArgb(110, 110, 110);
        private static Color triangleColor = Color.FromArgb(150, 0, 0);
        private static Color toolColor = Color.FromArgb(0, 25, 45);

        /** Obsolete public constants */
        public const int SPARE1 = UNUSED,
            SPARE2 = CUSTOM1,
            SPARE3 = CUSTOM2,
            SPARE4 = CUSTOM3,
            SPARE5 = CUSTOM4,
            SPARE6 = CUSTOM5,
            SPARE7 = CUSTOM6,
            SPARE8 = CUSTOM7,
            SPARE9 = 22;


        public ToolBar()
        {
            this.AutoSize = false;
            this.Height = 30;
            init();
            down = new bool[MAX_TOOLS];
            resetButtons();
            down[0] = true;
            ForeColor = Color.Black;
            BackColor = ImageK.backgroundColor;

            // addMouseListener(this);
            // addMouseMotionListener(this);
            this.MouseMove += OnMouseMove;
            this.MouseDown += OnMouseDown;
            this.MouseUp += OnMouseUp;

            instance = this;
            names[getNumTools() - 1] = "\"More Tools\" menu (switch toolsets or add tools)";
            icons[getNumTools() - 1] = "C900T0d18>T6d18>"; // ">>"
            addPopupMenus();
        }

        public void init()
        {
            dscale = Prefs.getGuiScale();
            scale = (int)Math.Round(dscale);
            if ((dscale >= 1.5 && dscale < 2.0) || (dscale >= 2.5 && dscale < 3.0))
                dscale = scale;
            if (dscale > 1.0)
            {
                buttonWidth = (int)((BUTTON_WIDTH - 2) * dscale);
                buttonHeight = (int)((BUTTON_HEIGHT - 2) * dscale);
                offset = (int)Math.Round((OFFSET - 1) * dscale);
            }
            else
            {
                buttonWidth = BUTTON_WIDTH;
                buttonHeight = BUTTON_HEIGHT;
                offset = OFFSET;
            }

            gapSize = GAP_SIZE;
            ps = new Size(buttonWidth * NUM_BUTTONS - (buttonWidth - gapSize), buttonHeight);
        }

        void addPopupMenus()
        {
            rectPopup = newPopupMenu();
            rectPopup.MouseDown += OnMouseDown;
            rectPopup.MouseUp += OnMouseUp;
            // rectItem = new CheckboxMenuItem("Rectangle", rectType == RECT_ROI);
            // rectItem.addItemListener(this);
            // rectPopup.add(rectItem);
            // roundRectItem = new CheckboxMenuItem("Rounded Rectangle", rectType == ROUNDED_RECT_ROI);
            // roundRectItem.addItemListener(this);
            // rectPopup.add(roundRectItem);
            // rotatedRectItem = new CheckboxMenuItem("Rotated Rectangle", rectType == ROTATED_RECT_ROI);
            // rotatedRectItem.addItemListener(this);
            // rectPopup.add(rotatedRectItem);
            this.Items.Add(rectPopup);

            // ovalPopup = newPopupMenu();
            // ovalItem = new CheckboxMenuItem("Oval selections", ovalType == OVAL_ROI);
            // ovalItem.addItemListener(this);
            // ovalPopup.add(ovalItem);
            // ellipseItem = new CheckboxMenuItem("Elliptical selections", ovalType == ELLIPSE_ROI);
            // ellipseItem.addItemListener(this);
            // ovalPopup.add(ellipseItem);
            // brushItem = new CheckboxMenuItem("Selection Brush Tool", ovalType == BRUSH_ROI);
            // brushItem.addItemListener(this);
            // ovalPopup.add(brushItem);
            // add(ovalPopup);
            //
            // pointPopup = newPopupMenu();
            // pointItem = new CheckboxMenuItem("Point Tool", !multiPointMode);
            // pointItem.addItemListener(this);
            // pointPopup.add(pointItem);
            // multiPointItem = new CheckboxMenuItem("Multi-point Tool", multiPointMode);
            // multiPointItem.addItemListener(this);
            // pointPopup.add(multiPointItem);
            // add(pointPopup);
            //
            // linePopup = newPopupMenu();
            // straightLineItem = new CheckboxMenuItem("Straight Line", lineType == LINE && !arrowMode);
            // straightLineItem.addItemListener(this);
            // linePopup.add(straightLineItem);
            // polyLineItem = new CheckboxMenuItem("Segmented Line", lineType == POLYLINE);
            // polyLineItem.addItemListener(this);
            // linePopup.add(polyLineItem);
            // freeLineItem = new CheckboxMenuItem("Freehand Line", lineType == FREELINE);
            // freeLineItem.addItemListener(this);
            // linePopup.add(freeLineItem);
            // arrowItem = new CheckboxMenuItem("Arrow tool", lineType == LINE && !arrowMode);
            // arrowItem.addItemListener(this);
            // linePopup.add(arrowItem);
            // add(linePopup);
            //
            // zoomPopup = newPopupMenu();
            // addMenuItem(zoomPopup, "Reset Zoom");
            // addMenuItem(zoomPopup, "Zoom In");
            // addMenuItem(zoomPopup, "Zoom Out");
            // addMenuItem(zoomPopup, "View 100%");
            // addMenuItem(zoomPopup, "Zoom To Selection");
            // addMenuItem(zoomPopup, "Scale to Fit");
            // addMenuItem(zoomPopup, "Set...");
            // addMenuItem(zoomPopup, "Maximize");
            // add(zoomPopup);
            //
            // pickerPopup = newPopupMenu();
            // addMenuItem(pickerPopup, "White/Black");
            // addMenuItem(pickerPopup, "Black/White");
            // addMenuItem(pickerPopup, "Red");
            // addMenuItem(pickerPopup, "Green");
            // addMenuItem(pickerPopup, "Blue");
            // addMenuItem(pickerPopup, "Yellow");
            // addMenuItem(pickerPopup, "Cyan");
            // addMenuItem(pickerPopup, "Magenta");
            // pickerPopup.addSeparator();
            // addMenuItem(pickerPopup, "Foreground...");
            // addMenuItem(pickerPopup, "Background...");
            // addMenuItem(pickerPopup, "Colors...");
            // addMenuItem(pickerPopup, "Color Picker...");
            // add(pickerPopup);
            //
            // switchPopup = newPopupMenu();
            // add(switchPopup);
        }

        private ToolStripDropDownButton newPopupMenu()
        {
            ToolStripDropDownButton popup = new ToolStripDropDownButton();
            // GUI.scalePopupMenu(popup);
            //System.out.println("newPopupMenu: "+popup.getFont());
            return popup;
        }

        // private void addMenuItem(PopupMenu menu, String command)
        // {
        //     MenuItem item = new MenuItem(command);
        //     item.addActionListener(this);
        //     menu.add(item);
        // }

        /** Returns the ID of the current tool (Toolbar.RECTANGLE, Toolbar.OVAL, etc.). */
        public static int getToolId()
        {
            int id = current;
            if (legacyMode)
            {
                if (id == CUSTOM1)
                    id = UNUSED;
                else if (id >= CUSTOM2)
                    id--;
            }

            return id;
        }

        /** Returns the ID of the tool whose name (the description displayed in the status bar)
			starts with the specified string, or -1 if the tool is not found. */
        public int getToolId(string name)
        {
            int tool = -1;
            for (int i = 0; i < getNumTools(); i++)
            {
                if (names[i] != null && names[i].StartsWith(name))
                {
                    tool = i;
                    break;
                }
            }

            return tool;
        }

        /** Returns a reference to the ImageJ toolbar. */
        public static ToolBar getInstance()
        {
            return instance;
        }

        private void drawButtons(Graphics g)
        {
            if (g == null)
                return;
            // Graphics2D g2d = (Graphics2D)g;
            // g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            // g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            // setStrokeWidth(g2d);
            for (int i = 0; i < LINE; i++)
                drawButton(g, i);
            drawButton(g, lineType);
            for (int i = POINT; i < getNumTools(); i++)
                drawButton(g, i);
        }

        private void fill3DRect(Graphics g, int x, int y, int width, int height, bool raised)
        {
            if (null == g) return;
            Brush brush;
            Pen pen;
            if (raised)
            {
                brush = new SolidBrush(gray);
                // g.setColor(gray);
            }
            else
            {
                brush = new SolidBrush(darker);
                // g.setColor(darker);
            }

            g.FillRectangle(brush, x + 1, y + 1, width - 2, height - 2);
            pen = new Pen(raised ? brighter : evenDarker);
            g.DrawLine(pen, x, y, x, y + height - 1);
            g.DrawLine(pen, x + 1, y, x + width - 2, y);
            pen = new Pen(raised ? evenDarker : brighter);
            g.DrawLine(pen, x + 1, y + height - 1, x + width - 1, y + height - 1);
            g.DrawLine(pen, x + width - 1, y, x + width - 1, y + height - 2);
        }

        private void drawButton(Graphics g, int tool)
        {
            if (g == null) return;
            if (legacyMode)
            {
                if (tool == UNUSED)
                    tool = CUSTOM1;
                else if (tool >= CUSTOM1)
                    tool++;
                if ((tool == POLYLINE && lineType != POLYLINE) || (tool == FREELINE && lineType != FREELINE))
                    return;
            }

            int index = toolIndex(tool);
            int x = index * buttonWidth + 1 * scale;
            if (tool >= CUSTOM1)
                x -= buttonWidth - gapSize;
            if (tool != UNUSED)
                fill3DRect(g, x, 1, buttonWidth, buttonHeight - 1, !down[tool]);
            _pen = new Pen(toolColor);
            _brush = new SolidBrush(toolColor);
            // g.setColor(toolColor);
            x = index * buttonWidth + offset;
            if (tool >= CUSTOM1)
                x -= buttonWidth - gapSize;
            int y = offset;
            if (dscale == 1.3)
            {
                x++;
                y++;
            }

            if (dscale == 1.4)
            {
                x += 2;
                y += 2;
            }

            if (down[tool])
            {
                x++;
                y++;
            }

            this._g = g;
            if (tool >= CUSTOM1 && tool <= getNumTools() && icons[tool] != null)
            {
                drawIcon(g, tool, x + 1 * scale, y + 1 * scale);
                return;
            }

            switch (tool)
            {
                case RECTANGLE:
                    xOffset = x;
                    yOffset = y;
                    if (rectType == ROUNDED_RECT_ROI)
                    {
                        // g.drawRoundRect(x - 1 * scale, y + 1 * scale, 17 * scale, 13 * scale, 8 * scale, 8 * scale);
                    }
                    else if (rectType == ROTATED_RECT_ROI)
                    {
                        // polyline(0, 10, 7, 0, 15, 6, 8, 16, 0, 10);
                    }
                    else
                    {
                        g.DrawRectangle(_pen, x - 1 * scale, y + 1 * scale, 17 * scale, 13 * scale);
                    }

                    drawTriangle(16, 16);
                    return;
                case OVAL:
                    xOffset = x;
                    yOffset = y;
                    if (ovalType == BRUSH_ROI)
                    {
                        yOffset = y - 1;
                        // polyline(6, 4, 8, 2, 12, 1, 15, 2, 16, 4, 15, 7, 12, 8, 9, 11, 9, 14, 6, 16, 2, 16, 0, 13, 1, 10, 4, 9, 6, 7, 6, 4);
                    }
                    else if (ovalType == ELLIPSE_ROI)
                    {
                        xOffset = x - 1;
                        yOffset = y + 1;
                        // polyline(11, 0, 13, 0, 14, 1, 15, 1, 16, 2, 17, 3, 17, 7, 12, 12, 11, 12, 10, 13, 8, 13, 7, 14, 4, 14, 3, 13, 2, 13, 1, 12, 1, 11, 0, 10, 0, 9, 1, 8, 1, 7, 6, 2, 7, 2, 8, 1, 10, 1, 11, 0);
                    }
                    else
                    {
                        g.DrawEllipse(_pen, x, y + 1 * scale, 17 * scale, 13 * scale);
                    }

                    drawTriangle(16, 16);
                    return;
                case POLYGON:
                    xOffset = x + 1;
                    yOffset = y + 2;
                    polyline(new int[] { 4, 0, 15, 0, 15, 1, 11, 5, 11, 6, 14, 11, 14, 12, 0, 12, 0, 4, 4, 0 });
                    return;
                case FREEROI:
                    xOffset = x;
                    yOffset = y + 3;
                    polyline(new int[]
                    {
                        2, 0, 5, 0, 7, 3, 10, 3, 12, 0, 15, 0, 17, 2, 17, 5, 16, 8, 13, 10, 11, 11, 6, 11, 4, 10, 1, 8,
                        0, 6, 0, 2, 2, 0
                    });
                    return;
                case LINE:
                    if (arrowMode)
                    {
                        xOffset = x;
                        yOffset = y;
                        m(1, 14);
                        d(14, 1);
                        m(6, 5);
                        d(14, 1);
                        m(10, 9);
                        d(14, 1);
                        m(6, 5);
                        d(10, 9);
                    }
                    else
                    {
                        xOffset = x - 1;
                        yOffset = y - 1;
                        m(1, 17);
                        d(18, 0);
                        drawDot(0, 16);
                        drawDot(17, 0);
                    }

                    drawTriangle(16, 16);
                    return;
                case POLYLINE:
                    xOffset = x;
                    yOffset = y;
                    polyline(new int[] { 15, 6, 11, 2, 1, 2, 1, 3, 7, 9, 2, 14 });
                    drawTriangle(14, 16);
                    return;
                case FREELINE:
                    xOffset = x;
                    yOffset = y;
                    polyline(new int[] { 16, 4, 14, 6, 12, 6, 9, 3, 8, 3, 6, 7, 2, 11, 1, 11 });
                    drawTriangle(14, 16);
                    return;
                case POINT:
                    xOffset = x;
                    yOffset = y;
                    if (multiPointMode)
                    {
                        drawPoint(1, 3);
                        drawPoint(9, 0);
                        drawPoint(15, 5);
                        drawPoint(10, 11);
                        drawPoint(2, 13);
                    }
                    else
                    {
                        m(1, 8);
                        d(6, 8);
                        d(6, 6);
                        d(10, 6);
                        d(10, 10);
                        d(6, 10);
                        d(6, 9);
                        m(8, 1);
                        d(8, 5);
                        m(11, 8);
                        d(15, 8);
                        m(8, 11);
                        d(8, 15);
                        m(8, 8);
                        d(8, 8);
                        _pen = new Pen(Roi.getColor());
                        m(7, 7);
                        d(9, 7);
                        m(7, 8);
                        d(9, 8);
                        m(7, 9);
                        d(9, 9);
                    }

                    drawTriangle(16, 16);
                    return;
                case WAND:
                    xOffset = x + 2;
                    yOffset = y + 1;
                    dot(4, 0);
                    m(2, 0);
                    d(3, 1);
                    d(4, 2);
                    m(0, 0);
                    d(1, 1);
                    m(0, 2);
                    d(1, 3);
                    d(2, 4);
                    dot(0, 4);
                    m(3, 3);
                    d(15, 15);
                    _pen = new Pen(Roi.getColor());
                    m(1, 2);
                    d(3, 2);
                    m(2, 1);
                    d(2, 3);
                    return;
                case TEXT:
                    xOffset = x;
                    yOffset = y;
                    m(1, 16);
                    d(9, 0);
                    d(16, 16);
                    m(0, 16);
                    d(2, 16);
                    m(15, 16);
                    d(17, 16);
                    m(4, 10);
                    d(13, 10);
                    return;
                case MAGNIFIER:
                    xOffset = x;
                    yOffset = y;
                    _g.DrawEllipse(_pen, x + 3, y, 13 * scale, 13 * scale);
                    m(5, 12);
                    d(-1, 18);
                    drawTriangle(15, 17);
                    return;
                case HAND:
                    xOffset = x;
                    yOffset = y;
                    polyline(new int[]
                    {
                        5, 17, 5, 16, 0, 11, 0, 8, 1, 8, 5, 11, 5, 2, 8, 2, 8, 8, 8, 0, 11, 0, 11, 8, 11, 1, 14, 1, 14,
                        9, 14, 3, 17, 3, 17, 12, 16, 13, 16, 17
                    });
                    return;
                case DROPPER:
                    // draw foreground/background rectangles	
                    _brush = new SolidBrush(backgroundColor);
                    _pen = new Pen(backgroundColor);
                    g.FillRectangle(_brush, x + 2 * scale, y + 3 * scale, 15 * scale, 16 * scale);
                    g.DrawRectangle(_pen, x, y + 2 * scale, 13 * scale, 13 * scale);
                    _brush = new SolidBrush(foregroundColor);
                    g.FillRectangle(_brush, x, y + 2 * scale, 13 * scale, 13 * scale);
                    // draw dropper icon		
                    xOffset = x + 3;
                    yOffset = y - 4;
                    _pen = new Pen(toolColor);
                    m(12, 2);
                    d(14, 2);
                    m(11, 3);
                    d(15, 3);
                    m(11, 4);
                    d(15, 4);
                    m(8, 5);
                    d(15, 5);
                    m(9, 6);
                    d(14, 6);
                    polyline(new int[] { 10, 7, 12, 7, 12, 9 });
                    polyline(new int[] { 9, 6, 2, 13, 2, 15, 4, 15, 11, 8 });
                    _brush = new SolidBrush(gray);
                    polygon(new int[] { 9, 6, 2, 13, 2, 15, 4, 15, 11, 8 });
                    drawTriangle(12, 21);
                    return;
                case ANGLE:
                    xOffset = x;
                    yOffset = y + 3;
                    m(0, 11);
                    d(13, -1);
                    m(0, 11);
                    d(16, 11);
                    m(10, 11);
                    d(10, 8);
                    m(9, 7);
                    d(9, 6);
                    dot(8, 5);
                    drawDot(13, -2);
                    drawDot(16, 10);
                    return;
            }
        }

        void drawTriangle(int x, int y)
        {
            _pen = new Pen(triangleColor);
            xOffset += x * scale;
            yOffset += y * scale;
            m(0, 0);
            d(4, 0);
            m(1, 1);
            d(3, 1);
            m(2, 2);
            d(2, 2);
        }

        void drawDot(int x, int y)
        {
            _g.FillRectangle(_brush, xOffset + x * scale, yOffset + y * scale, 2 * scale, 2 * scale);
        }

        void drawPoint(int x, int y)
        {
            _pen = new Pen(toolColor);
            m(x - 3, y);
            d(x + 3, y);
            m(x, y - 3);
            d(x, y + 3);
            _brush = new SolidBrush(Roi.getColor());
            dot(x, y);
            dot(x - 1, y - 1);
        }

        void drawIcon(Graphics g, int tool, int x, int y)
        {
            if (null==g) return;
            icon = icons[tool];
            if (icon==null) return;
            this.icon = icon;
            int x1, y1, x2, y2;
            pc = 0;
            if (icon.Trim().StartsWith("icon:"))
            {
                String path = IJ.getDir("macros")+"toolsets/icons/"+icon.Substring(icon.IndexOf(":")+1);
                //todo:
                // try
                // {
                //     BufferedImage bi = ImageIO.read(new File(path));
                //     if (scale==1)
                //         g.drawImage(bi, x-5, y-5, null);
                //     else
                //     {
                //         int size = Math.Max(bi.getWidth(), bi.getHeight());
                //         g.drawImage(bi, x-5*scale, y-5*scale, size*scale, size*scale, null);
                //     }
                // }
                // catch (Exception e)
                // {
                //     IJ.error("Toolbar", "Error reading tool icon:\n"+path);
                // }
            }
            else
            {
                while (true)
                {
                    char command = icon[pc++];
                    if (pc>=icon.Length) break;
                    switch (command)
                    {
                        case 'B': x+=v(); y+=v(); break;  // adjust base
                        case 'N': x-=v(); y-=v(); break;  // adjust base negatively
                        case 'R': g.DrawRectangle(_pen, x+v(), y+v(), v(), v()); break;  // rectangle
                        case 'F': g.FillRectangle(_brush, x+v(), y+v(), v(), v()); break;  // filled rectangle
                        case 'O': g.DrawEllipse(_pen, x+v(), y+v(), v(), v()); break;  // oval
                        case 'V': case 'o': g.FillEllipse(_brush, x+v(), y+v(), v(), v()); break;  // filled oval
                        case 'C': // set color
                            int saveScale = scale;
                            scale = 1;
                            int v1 = v(), v2 = v(), v3 = v();
                            int red = v1*16, green = v2*16, blue = v3*16;
                            if (red>255) red=255; if (green>255) green=255; if (blue>255) blue=255;
                            Color color = v1==1&&v2==2&&v3==3 ? foregroundColor : Color.FromArgb(red, green, blue);
                            _pen = new Pen(color);
                            _brush = new SolidBrush(color);
                            // g.setColor(color);
                            scale = saveScale;
                            break;
                        case 'L': g.DrawLine(_pen, x+v(), y+v(), x+v(), y+v()); break; // line
                        case 'D': g.FillRectangle(_brush, x+v(), y+v(), scale, scale); break; // dot
                        case 'P': // polyline
                            List<PointF> p = new List<PointF>();
                            p.Add(new PointF(x+v(), y+v()));
                            while (true)
                            {
                                x2=v(); if (x2==0) break;
                                y2=v(); if (y2==0) break;
                                p.Add(new PointF(x+x2, y+y2));
                            }
                            g.DrawPolygon(_pen, p.ToArray());
                            break;
                        case 'G':
                        case 'H':// polygon or filled polygon
                            p = new List<PointF>();
                            p.Add(new PointF(x+v(), y+v()));
                            while (true)
                            {
                                x2=v(); y2=v();
                                if (x2==0 && y2==0 && p.Count>2)
                                    break;
                                p.Add(new Point(x+x2, y+y2));
                            }
                            if (command=='G')
                                g.DrawPolygon(_pen, p.ToArray());
                            else
                                g.FillPolygon(_brush, p.ToArray());
                            break;
                        case 'T': // text (one character)
                            x2 = x+v()-2;
                            y2 = y+v();
                            int size = v()*10+v()+1;
                            char[] c = new char[1];
                            c[0] = pc<icon.Length ? icon[pc++] : 'e';
                            // g.setFont(new Font("SansSerif", Font.PLAIN, size));
                            g.DrawString(new String(c), new Font("SansSerif", size, FontStyle.Regular),_brush, x2, y2);
                            break;
                        default: break;
                    }
                    if (pc>=icon.Length) break;
                }
            }
            if (menus[tool]!=null && menus[tool].DropDownItems.Count>0)
            {
                xOffset = x; yOffset = y;
                drawTriangle(15, 16);
            }
        }

        int v()
        {
            if (pc>=icon.Length) return 0;
            char c = icon[pc++];
            switch (c)
            {
                case '0': return 0*scale;
                case '1': return 1*scale;
                case '2': return 2*scale;
                case '3': return 3*scale;
                case '4': return 4*scale;
                case '5': return 5*scale;
                case '6': return 6*scale;
                case '7': return 7*scale;
                case '8': return 8*scale;
                case '9': return 9*scale;
                case 'a': return 10*scale;
                case 'b': return 11*scale;
                case 'c': return 12*scale;
                case 'd': return 13*scale;
                case 'e': return 14*scale;
                case 'f': return 15*scale;
                case 'g': return 16*scale;
                case 'h': return 17*scale;
                case 'i': return 18*scale;
                case 'j': return 19*scale;
                case 'k': return 20*scale;
                case 'l': return 21*scale;
                case 'm': return 22*scale;
                case 'n': return 23*scale;
                default: return 0;
            }
        }

        private void showMessage(int tool)
        {
            if (IJ.statusBarProtected())
                return;
            if (tool >= UNUSED && tool < getNumTools() && names[tool] != null)
            {
                String name = names[tool];
                int index = name.IndexOf("Action Tool");
                if (index != -1)
                    name = name.Replace("Action Tool", "Tool");
                else
                {
                    index = name.IndexOf("Menu Tool");
                    if (index != -1)
                        name = name.Substring(0, index + 4);
                }

                IJ.ShowStatus(name);
                return;
            }

            string hint = " (alt or long click to switch)";
            string hint2 = " (alt or long click to switch; double click to configure)";
            switch (tool)
            {
                case RECTANGLE:
                    if (rectType == ROUNDED_RECT_ROI)
                        IJ.ShowStatus("Rectangle, *rounded rect* or rotated rect" + hint);
                    else if (rectType == ROTATED_RECT_ROI)
                        IJ.ShowStatus("Rectangle, rounded rect or *rotated rect*" + hint);
                    else
                        IJ.ShowStatus("*Rectangle*, rounded rect or rotated rect" + hint);
                    return;
                case OVAL:
                    if (ovalType == BRUSH_ROI)
                        IJ.ShowStatus("Oval, elliptical or *brush* selections" + hint);
                    else if (ovalType == ELLIPSE_ROI)
                        IJ.ShowStatus("Oval, *elliptical* or brush selections" + hint);
                    else
                        IJ.ShowStatus("*Oval*, elliptical or brush selections" + hint);
                    return;
                case POLYGON:
                    IJ.ShowStatus("Polygon selections");
                    return;
                case FREEROI:
                    IJ.ShowStatus("Freehand selections");
                    return;
                case LINE:
                    if (arrowMode)
                        IJ.ShowStatus("Straight, segmented or freehand lines, or *arrows*" + hint);
                    else
                        IJ.ShowStatus("*Straight*, segmented or freehand lines, or arrows" + hint);
                    return;
                case POLYLINE:
                    IJ.ShowStatus("Straight, *segmented* or freehand lines, or arrows" + hint);
                    return;
                case FREELINE:
                    IJ.ShowStatus("Straight, segmented or *freehand* lines, or arrows" + hint);
                    return;
                case POINT:
                    if (multiPointMode)
                        IJ.ShowStatus("*Multi-point* or point" + hint2);
                    else
                        IJ.ShowStatus("*Point* or multi-point" + hint2);
                    return;
                case WAND:
                    IJ.ShowStatus("Wand (tracing) tool");
                    return;
                case TEXT:
                    IJ.ShowStatus("Text tool (double-click to configure)");
                    return;
                case MAGNIFIER:
                    IJ.ShowStatus("Magnifying glass (or \"+\" and \"-\" keys; alt or long click for menu)");
                    return;
                case HAND:
                    IJ.ShowStatus("Scrolling tool (or press space bar and drag)");
                    return;
                case DROPPER:
                    String fg = foregroundColor.R + "," + foregroundColor.G + "," + foregroundColor.B;
                    String bg = backgroundColor.R + "," + backgroundColor.G + "," + backgroundColor.B;
                    IJ.ShowStatus("Color picker " + fg + "/" + bg + " (alt or long click for menu)");
                    return;
                case ANGLE:
                    IJ.ShowStatus("Angle tool");
                    return;
                default:
                    // IJ.ShowStatus("ImageJ " + IJ.getVersion() + " / Java " + System.getProperty("java.version") + (IJ.is64Bit() ? " (64-bit)" : " (32-bit)"));
                    return;
            }
        }

        private void m(int x, int y)
        {
            this.x = xOffset + x * scale;
            this.y = yOffset + y * scale;
        }

        private void d(int x, int y)
        {
            x *= scale;
            y *= scale;
            x += xOffset;
            y += yOffset;
            _g.DrawLine(_pen, this.x, this.y, x, y);
            this.x = x;
            this.y = y;
        }

        private void dot(int x, int y)
        {
            _g.FillRectangle(_brush, xOffset + x * scale, yOffset + y * scale, 1 * scale, 1 * scale);
        }

        private void polyline(int[] values)
        {
            int n = values.Length / 2;
            PointF[] p = new PointF[n];
            for (int i = 0; i < n; i++)
                p[i] = new PointF(values[i * 2] * scale + xOffset, values[i * 2 + 1] * scale + yOffset);
            _g.DrawPolygon(_pen, p);
        }

        private void polygon(int[] values)
        {
            int n = values.Length / 2;
            PointF[] p = new PointF[n];
            for (int i = 0; i < n; i++)
                p[i] = new PointF(values[i * 2] * scale + xOffset, values[i * 2 + 1] * scale + yOffset);
            _g.FillPolygon(_brush, p);
        }



        private void resetButtons()
        {
            for (int i = 0; i < getNumTools(); i++)
                down[i] = false;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            drawButtons(e.Graphics);
        }

        public bool setTool(string name)
        {
            if (name==null) return false;
            if (name.IndexOf(" Tool")!=-1)
            { // macro tool?
                for (int i = UNUSED; i<getNumTools(); i++)
                {
                    if (name.Equals(names[i]))
                    {
                        setTool(i);
                        return true;
                    }
                }
            }
            name = name.ToLower();
            bool ok = true;
            if (name.IndexOf("round")!=-1)
            {
                rectType = ROUNDED_RECT_ROI;
                setTool(RECTANGLE);
            }
            else if (name.IndexOf("rot")!=-1)
            {
                rectType = ROTATED_RECT_ROI;
                setTool(RECTANGLE);
            }
            else if (name.IndexOf("rect")!=-1)
            {
                rectType = RECT_ROI;
                setTool(RECTANGLE);
            }
            else if (name.IndexOf("oval")!=-1)
            {
                ovalType = OVAL_ROI;
                setTool(OVAL);
            }
            else if (name.IndexOf("ellip")!=-1)
            {
                ovalType = ELLIPSE_ROI;
                setTool(OVAL);
            }
            else if (name.IndexOf("brush")!=-1)
            {
                ovalType = BRUSH_ROI;
                setTool(OVAL);
            }
            else if (name.IndexOf("polygon")!=-1)
                setTool(POLYGON);
            else if (name.IndexOf("polyline")!=-1)
                setTool(POLYLINE);
            else if (name.IndexOf("freeline")!=-1)
                setTool(FREELINE);
            else if (name.IndexOf("line")!=-1)
            {
                arrowMode = false;
                setTool(LINE);
            }
            else if (name.IndexOf("arrow")!=-1)
            {
                arrowMode = true;
                setTool(LINE);
            }
            else if (name.IndexOf("free")!=-1)
                setTool(FREEROI);
            else if (name.IndexOf("multi")!=-1)
            {
                multiPointMode = true;
                Prefs.multiPointMode = true;
                setTool(POINT);
            }
            else if (name.IndexOf("point")!=-1)
            {
                multiPointMode = false;
                Prefs.multiPointMode = false;
                setTool(POINT);
            }
            else if (name.IndexOf("wand")!=-1)
                setTool(WAND);
            else if (name.IndexOf("text")!=-1)
                setTool(TEXT);
            else if (name.IndexOf("hand")!=-1)
                setTool(HAND);
            else if (name.IndexOf("zoom")!=-1)
                setTool(MAGNIFIER);
            else if (name.IndexOf("dropper")!=-1||name.IndexOf("color")!=-1)
                setTool(DROPPER);
            else if (name.IndexOf("angle")!=-1)
                setTool(ANGLE);
            else
                ok = false;
            return ok;
        }

        /** Returns the name of the current tool. */
        public static string getToolName()
        {
            string name = instance.getName(current);
            if (current >= UNUSED && current < instance.getNumTools() && instance.names[current] != null)
                name = instance.names[current];
            return name != null ? name : "";
        }

        /** Returns the name of the specified tool. */
        string getName(int id)
        {
            switch (id)
            {
                case RECTANGLE:
                    switch (rectType)
                    {
                        case RECT_ROI: return "rectangle";
                        case ROUNDED_RECT_ROI: return "roundrect";
                        case ROTATED_RECT_ROI: return "rotrect";
                        default: return null;
                    }
                case OVAL:
                    switch (ovalType)
                    {
                        case OVAL_ROI: return "oval";
                        case ELLIPSE_ROI: return "ellipse";
                        case BRUSH_ROI: return "brush";
                        default: return null;
                    }
                case POLYGON: return "polygon";
                case FREEROI: return "freehand";
                case LINE: return arrowMode ? "arrow" : "line";
                case POLYLINE: return "polyline";
                case FREELINE: return "freeline";
                case ANGLE: return "angle";
                case POINT: return Prefs.multiPointMode ? "multipoint" : "point";
                case WAND: return "wand";
                case TEXT: return "text";
                case HAND: return "hand";
                case MAGNIFIER: return "zoom";
                case DROPPER: return "dropper";
                default: return null;
            }
        }

        /** Returns the rectangle tool type (RECT_ROI, ROUNDED_RECT_ROI or ROTATED_RECT_ROI). */
        public static int getRectToolType()
        {
            return rectType;
        }

        /** Returns the oval tool type (OVAL_ROI, ELLIPSE_ROI or BRUSH_ROI). */
        public static int getOvalToolType()
        {
            return ovalType;
        }

        /** Returns the button width (button spacing). */
        public static int getButtonSize()
        {
            return buttonWidth;
        }


        public void repaintTool(int tool)
        {
            ToolBar tb = getInstance();
            if (tb != null)
            {
                Graphics g = tb.CreateGraphics();
                if (IJ.debugMode) IJ.log("Toolbar.repaintTool: " + tool + " " + g);
                if (g == null) return;
                // if (dscale > 1.0)
                //     tb.setStrokeWidth((Graphics2D)g);
                // ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                tb.drawButton(g, tool);
                if (g != null) g.Dispose();
            }
        }

        int toolIndex(int tool)
        {
            switch (tool)
            {
                case RECTANGLE: return 0;
                case OVAL: return 1;
                case POLYGON: return 2;
                case FREEROI: return 3;
                case LINE: return 4;
                case POLYLINE: return 4;
                case FREELINE: return 4;
                case POINT: return 6;
                case WAND: return 7;
                case TEXT: return 8;
                case MAGNIFIER: return 9;
                case HAND: return 10;
                case DROPPER: return 11;
                case ANGLE: return 5;
                case UNUSED: return 12;
                default: return tool - 2;
            }
        }

        private int toolID(int x)
        {
            if (x > buttonWidth * 12 + gapSize)
                x -= gapSize;
            int index = x / buttonWidth;
            switch (index)
            {
                case 0: return RECTANGLE;
                case 1: return OVAL;
                case 2: return POLYGON;
                case 3: return FREEROI;
                case 4: return lineType;
                case 5: return ANGLE;
                case 6: return POINT;
                case 7: return WAND;
                case 8: return TEXT;
                case 9: return MAGNIFIER;
                case 10: return HAND;
                case 11: return DROPPER;
                default: return index + 3;
            }
        }

        public void setTool(int tool)
        {
            if (tool == UNUSED) //  "Unused" (blank) tool replaced with gap in 1.48h
                tool = CUSTOM1;
            else if (legacyMode && tool >= CUSTOM1)
                tool++;
            if (IJ.debugMode) IJ.log("Toolbar.setTool: " + tool);
            if ((tool == current && !(tool == RECTANGLE || tool == OVAL || tool == POINT)) || tool < 0 ||
                tool >= getNumTools() - 1)
                return;
            if (tool >= CUSTOM1 && tool <= getNumTools() - 2)
            {
                if (names[tool] == null)
                    names[tool] = "Spare tool"; // enable tool
                if (names[tool].IndexOf("Action Tool") != -1)
                    return;
            }

            if (isLine(tool)) lineType = tool;
            setTool2(tool);
        }

        private void setTool2(int tool)
        {
            if (!isValidTool(tool)) return;
            string previousName = getToolName();
            previousTool = current;
            current = tool;
            Graphics g = this.CreateGraphics();
            if (g == null)
                return;
            down[current] = true;
            if (current != previousTool)
                down[previousTool] = false;
            // Graphics2D g2d = (Graphics2D)g;
            // g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            // setStrokeWidth(g2d);
            drawButton(g, previousTool);
            drawButton(g, current);
            if (null == g) return;
            // g.dispose();
            showMessage(current);
            if (Recorder.OnRecord)
            {
                //todo:
                // string name = getName(current);
                // if (name != null && name.Equals("dropper")) disableRecording = true;
                // if (name != null && !disableRecording)
                // {
                //     IJ.wait(100); // workaround for OSX/Java 8 bug
                //     Recorder.record("setTool", name);
                // }
                // if (name != null && !name.Equals("dropper")) disableRecording = false;
            }

            if (legacyMode)
            {
                Invalidate();
            }

            if (!previousName.Equals(getToolName()))
            {
                // IJ.notifyEventListeners(IJEventListener.TOOL_CHANGED); 
                Invalidate();
            }
        }

        bool isValidTool(int tool)
        {
            if (tool < 0 || tool >= getNumTools())
                return false;
            if (tool >= CUSTOM1 && tool < getNumTools() && names[tool] == null)
                return false;
            return true;
        }

        public static Color getForegroundColor()
        {
            return foregroundColor;
        }

        public static void setForegroundColor(Color c)
        {
            if (c == null)
                return;
            foregroundColor = c;
            foregroundValue = Double.NaN;
            //todo:
            // IJ.notifyEventListeners(IJEventListener.FOREGROUND_COLOR_CHANGED);
            // if (instance == null)
            //     return;
            // repaintTool(DROPPER);
            // for (int i = CUSTOM1; i <= instance.getNumTools() - 2; i++)
            // {
            //     if (instance.icons[i] != null && instance.icons[i].contains("C123"))
            //         repaintTool(i);  // some of this tool's icon is drawn in the foreground color
            // }
            // ColorPicker.update();
            // if (!IJ.isMacro()) setRoiColor(c);
        }

        public static Color getBackgroundColor()
        {
            return backgroundColor;
        }

        public static void setBackgroundColor(Color c)
        {
            if (c != null)
            {
                backgroundColor = c;
                backgroundValue = Double.NaN;
                //todo:
                // repaintTool(DROPPER);
                // ColorPicker.update();
                // IJ.notifyEventListeners(IJEventListener.BACKGROUND_COLOR_CHANGED);
            }
        }

        public static double getForegroundValue()
        {
            return foregroundValue;
        }

        /** Sets the foreground color to grayscale, where value
            is between 0 (black) and 255 (white). */
        public static void setForegroundValue(double value)
        {
            if (value >= 0)
            {
                int v = (int)value;
                if (v > 255) v = 255;
                setForegroundColor(Color.FromArgb(v, v, v));
            }
            foregroundValue = value;
        }

        public static double getBackgroundValue()
        {
            return backgroundValue;
        }

        /** Sets the background color to grayscale, where value
            is between 0 (black) and 255 (white). */
        public static void setBackgroundValue(double value)
        {
            if (value >= 0)
            {
                int v = (int)value;
                if (v > 255) v = 255;
                setBackgroundColor(Color.FromArgb(v, v, v));
            }
            backgroundValue = value;
        }

        private static void setRoiColor(Color c)
        {
            ImagePlus imp = WindowManager.getCurrentImage();
            if (imp == null) return;
            Roi roi = imp.getRoi();
            if (roi != null && (roi.isDrawingTool()))
            {
                roi.setStrokeColor(c);
                imp.draw();
            }
        }

        /** Returns the size of the selection brush tool, or 0 if the brush tool is not enabled. */
        public static int getBrushSize()
        {
            if (ovalType == BRUSH_ROI)
                return brushSize;
            else
                return 0;
        }

        /** Set the size of the selection brush tool, in pixels. */
        public static void setBrushSize(int size)
        {
            brushSize = size;
            if (brushSize < 1) brushSize = 1;
            Prefs.set(BRUSH_SIZE, brushSize);
        }

        /** Returns the rounded rectangle arc size, or 0 if the rounded rectangle tool is not enabled. */
        public static int getRoundRectArcSize()
        {
            if (rectType == ROUNDED_RECT_ROI)
                return arcSize;
            else
                return 0;
        }

        /** Sets the rounded rectangle corner diameter (pixels). */
        public static void setRoundRectArcSize(int size)
        {
            throw new NotImplementedException();
            // if (size <= 0)
            //     rectType = RECT_ROI;
            // else
            // {
            //     arcSize = size;
            //     Prefs.set(CORNER_DIAMETER, arcSize);
            // }
            // repaintTool(RECTANGLE);
            // ImagePlus imp = WindowManager.getCurrentImage();
            // Roi roi = imp != null ? imp.getRoi() : null;
            // if (roi != null && roi.getType() == Roi.RECTANGLE)
            //     roi.setCornerDiameter(rectType == ROUNDED_RECT_ROI ? arcSize : 0);
        }

        private bool inGap(int x)
        {
            return x >= (buttonWidth * 12) && x < (buttonWidth * 12 + gapSize);
        }

        public void triggerPopupMenu(int newTool, MouseEventArgs e, bool isRightClick, bool isLongPress)
        {
            mpPrevious = current;
            if (isMacroTool(newTool))
            {
                //todo:
                // string name = names[newTool];
                // if (newTool == UNUSED || name.contains("Unused Tool"))
                //     return;
                // if (name.indexOf("Action Tool") != -1)
                // {
                //     if (e.isPopupTrigger() || e.isMetaDown())
                //     {
                //         name = name.endsWith(" ") ? name : name + " ";
                //         tools[newTool].runMacroTool(name + "Options");
                //     }
                //     else
                //     {
                //         //drawTool(newTool, true);
                //         //IJ.wait(50);
                //         drawTool(newTool, false);
                //         runMacroTool(newTool);
                //     }
                //     return;
                // }
                // else
                // {
                //     name = name.endsWith(" ") ? name : name + " ";
                //     tools[newTool].runMacroTool(name + "Selected");
                // }
            }

            if (!isLongPress)
                setTool2(newTool);
            int x = e.X;
            int y = e.Y;

            //todo:
            // if (current == RECTANGLE && isRightClick)
            // {
            //     rectItem.setState(rectType == RECT_ROI);
            //     roundRectItem.setState(rectType == ROUNDED_RECT_ROI);
            //     rotatedRectItem.setState(rectType == ROTATED_RECT_ROI);
            //     if (IJ.isMacOSX()) IJ.wait(10);
            //     rectPopup.show(e.getComponent(), x, y);
            //     mouseDownTime = 0L;
            // }
            // if (current == OVAL && isRightClick)
            // {
            //     ovalItem.setState(ovalType == OVAL_ROI);
            //     ellipseItem.setState(ovalType == ELLIPSE_ROI);
            //     brushItem.setState(ovalType == BRUSH_ROI);
            //     if (IJ.isMacOSX()) IJ.wait(10);
            //     ovalPopup.show(e.getComponent(), x, y);
            //     mouseDownTime = 0L;
            // }
            // if (current == POINT && isRightClick)
            // {
            //     pointItem.setState(!multiPointMode);
            //     multiPointItem.setState(multiPointMode);
            //     if (IJ.isMacOSX()) IJ.wait(10);
            //     pointPopup.show(e.getComponent(), x, y);
            //     mouseDownTime = 0L;
            // }
            // if (isLine(current) && isRightClick)
            // {
            //     straightLineItem.setState(lineType == LINE && !arrowMode);
            //     polyLineItem.setState(lineType == POLYLINE);
            //     freeLineItem.setState(lineType == FREELINE);
            //     arrowItem.setState(lineType == LINE && arrowMode);
            //     if (IJ.isMacOSX()) IJ.wait(10);
            //     linePopup.show(e.getComponent(), x, y);
            //     mouseDownTime = 0L;
            // }
            // if (current == MAGNIFIER && isRightClick)
            // {
            //     zoomPopup.show(e.getComponent(), x, y);
            //     mouseDownTime = 0L;
            // }
            // if (current == DROPPER && isRightClick)
            // {
            //     pickerPopup.show(e.getComponent(), x, y);
            //     mouseDownTime = 0L;
            // }
            // if (isMacroTool(current) && isRightClick)
            // {
            //     String name = names[current].endsWith(" ") ? names[current] : names[current] + " ";
            //     tools[current].runMacroTool(name + "Options");
            // }
            // if (isPlugInTool(current) && isRightClick)
            // {
            //     tools[current].showPopupMenu(e, this);
            // }
        }

        public void OnMouseDown(object? sender, MouseEventArgs e)
        {
            var isButton = sender is ToolStripDropDownButton button;
            bool disablePopup = false;
            int x = e.X;
            if (inGap(x))
                return;
            int newTool = toolID(x);
            if (newTool == getNumTools() - 1)
            {
                //todo:
                //showSwitchPopupMenu(e);
                return;
            }

            if (!isValidTool(newTool))
                return;
            //todo:
            // if (menus[newTool] != null && menus[newTool].getItemCount() > 0)
            // {
            // 	menus[newTool].show(e.getComponent(), e.X, e.Y);
            // 	return;
            // }
            MouseButtons flags = e.Button;
            bool isRightClick = flags == MouseButtons.Right;
            bool doubleClick = newTool == current && (e.Clicks > 1);
            mouseDownTime = DateTime.Now.Ticks;
            if (!doubleClick || isRightClick)
            {
                triggerPopupMenu(newTool, e, isRightClick, false);
                if (isRightClick) mouseDownTime = 0L;
            }
            else if (!isRightClick)
            {
                //double click
                //todo:
                // if (isMacroTool(current))
                // {
                // 	string name = names[current].EndsWith(" ") ? names[current] : names[current] + " ";
                // 	tools[current].runMacroTool(name + "Options");
                // 	return;
                // }
                // if (isPlugInTool(current))
                // {
                // 	tools[current].showOptionsDialog();
                // 	return;
                // }
                ImagePlus imp = WindowManager.getCurrentImage();
                switch (current)
                {
                    case RECTANGLE:
                        if (rectType == ROUNDED_RECT_ROI)
                            IJ.doCommand("Rounded Rect Tool...");
                        else
                        {
                            disablePopup = true;
                            IJ.doCommand("Roi Defaults...");
                        }

                        break;
                    // case OVAL:
                    // 	showBrushDialog();
                    // 	break;
                    // case MAGNIFIER:
                    // 	if (imp != null)
                    // 	{
                    // 		ImageCanvas ic = imp.getCanvas();
                    // 		if (ic != null) ic.unzoom();
                    // 	}
                    // 	break;
                    // case LINE:
                    // case POLYLINE:
                    // case FREELINE:
                    // 	if (current == LINE && arrowMode)
                    // 	{
                    // 		IJ.doCommand("Arrow Tool...");
                    // 	}
                    // 	else
                    // 		IJ.runPlugIn("ij.plugin.frame.LineWidthAdjuster", "");
                    // 	break;
                    // case ANGLE:
                    // 	showAngleDialog();
                    // 	break;
                    // case POINT:
                    // 	IJ.doCommand("Point Tool...");
                    // 	break;
                    // case WAND:
                    // 	IJ.doCommand("Wand Tool...");
                    // 	break;
                    // case TEXT:
                    // 	IJ.run("Fonts...");
                    // 	break;
                    // case DROPPER:
                    // 	IJ.doCommand("Color Picker...");
                    // 	setTool2(mpPrevious);
                    // 	break;
                    default:
                        break;
                }
            }
            //
            // if (!isRightClick && longClickDelay > 0 && !disablePopup)
            // {
            // 	if (pressTimer == null)
            // 		pressTimer = new Timer();
            // 	pressTimer.schedule(new TimerTask()
            // 	{
            // 	public void run()
            // 	{
            // 		if (pressTimer != null)
            // 		{
            // 			pressTimer.cancel();
            // 			pressTimer = null;
            // 		}
            // 		triggerPopupMenu(newTool, e, true, true);
            // 	}
            // }, longClickDelay);
        }

        public void OnMouseUp(object? sender, MouseEventArgs e)
        {
            if (pressTimer != null)
            {
                pressTimer.Stop();
                pressTimer = null;
            }
        }

        private void OnMouseMove(object? sender, MouseEventArgs e)
        {
            int x = e.X;
            if (inGap(x))
                IJ.ShowStatus("");
            else
                showMessage(toolID(x));
        }

        public int getNumTools()
        {
            return NUM_TOOLS + nExtraTools;
        }

        bool isMacroTool(int tool)
        {
            return false;
            //todo:
            // return tool >= CUSTOM1 && tool < getNumTools() && names[tool] != null && (tools[tool] is MacroToolRunner);
        }

        bool isLine(int tool)
        {
            return tool == LINE || tool == POLYLINE || tool == FREELINE;
        }

        /** Adds a tool to the toolbar. The 'toolTip' string is displayed in the status bar
         when the mouse is over the tool icon. The 'toolTip' string may include icon 
        (http://imagej.nih.gov/ij/developer/macro/macros.html#tools).
        Returns the tool ID, or -1 if all tool slots are in use. */
        public int addTool(string toolTip)
        {
            int index = toolTip.IndexOf('-');
            bool hasIcon = index >= 0 && (toolTip.Length - index) > 4;
            int tool = -1;
            for (int i = CUSTOM1; i <= getNumTools() - 2; i++)
            {
                if (names[i] == null || toolTip.StartsWith(names[i]))
                {
                    tool = i;
                    break;
                }
            }

            if (tool == CUSTOM1)
                legacyMode = toolTip.StartsWith("Select and Transform Tool"); //TrakEM2
            if (tool == -1 && (nExtraTools < MAX_EXTRA_TOOLS))
            {
                nExtraTools++;
                names[getNumTools() - 1] = names[getNumTools() - 2];
                icons[getNumTools() - 1] = icons[getNumTools() - 2];
                names[getNumTools() - 2] = null;
                icons[getNumTools() - 2] = null;
                ps = new Size(buttonWidth * NUM_BUTTONS - (buttonWidth - gapSize) + nExtraTools * buttonWidth,
                    buttonHeight);
                IJ.GetInstance().Refresh();
                tool = getNumTools() - 2;
            }

            if (tool == -1)
            {
                if (addingSingleTool)
                    tool = getNumTools() - 2;
                else
                    return -1;
            }

            if (hasIcon)
            {
                icons[tool] = toolTip.Substring(index + 1);
                if (index > 0 && toolTip[index - 1] == ' ')
                    names[tool] = toolTip.Substring(0, index - 1);
                else
                    names[tool] = toolTip.Substring(0, index);
            }
            else
            {
                if (toolTip.EndsWith("-"))
                    toolTip = toolTip.Substring(0, toolTip.Length - 1);
                else if (toolTip.EndsWith("- "))
                    toolTip = toolTip.Substring(0, toolTip.Length - 2);
                names[tool] = toolTip;
            }

            if (tool == current &&
                (names[tool].IndexOf("Action Tool") != -1 || names[tool].IndexOf("Unused Tool") != -1))
                setTool(RECTANGLE);
            if (names[tool].EndsWith(" Menu Tool"))
                installMenu(tool);
            if (IJ.debugMode) IJ.log("Toolbar.addTool: " + tool + " " + toolTip);
            return tool;
        }

        void installMenu(int tool)
        {
            // Program pgm = macroInstaller.getProgram();
            // Hashtable h = pgm.getMenus();
            // if (h == null) return;
            // String[] commands = (String[])h.get(names[tool]);
            // if (commands == null)
            //     return;
            // if (menus[tool] == null)
            // {
            //     menus[tool] = new PopupMenu("");
            //     GUI.scalePopupMenu(menus[tool]);
            //     add(menus[tool]);
            // }
            // else
            //     menus[tool].removeAll();
            // for (int i = 0; i < commands.length; i++)
            // {
            //     if (commands[i].equals("-"))
            //         menus[tool].addSeparator();
            //     else if (commands[i].startsWith("-"))
            //         menus[tool].addSeparator();
            //     else
            //     {
            //         boolean disable = commands[i].startsWith("*");
            //         String command = commands[i];
            //         if (disable)
            //             command = command.substring(1);
            //         MenuItem mi = new MenuItem(command);
            //         if (disable)
            //             mi.setEnabled(false);
            //         mi.addActionListener(this);
            //         menus[tool].add(mi);
            //     }
            // }
            // if (tool == current) setTool(RECTANGLE);
        }

        /** Adds a plugin tool to the first available toolbar slot,
        or to the last slot if the toolbar is full. */
        public static void addPlugInTool(PlugInTool tool)
        {
            if (instance == null) return;
            string nameAndIcon = tool.getToolName() + " - " + tool.getToolIcon();
            instance.addingSingleTool = true;
            int id = instance.addTool(nameAndIcon);
            instance.addingSingleTool = false;
            if (id != -1)
            {
                instance.tools[id] = tool;
                if (instance.menus[id] != null)
                    instance.menus[id].DropDownItems.Clear();
                instance.repaintTool(id);
                if (!instance.installingStartupTool)
                    instance.setTool(id);
                else
                    instance.installingStartupTool = false;
                instance.setPrefs(id);
            }
        }

        public static PlugInTool getPlugInTool()
        {
            PlugInTool tool = null;
            if (instance == null)
                return null;
            if (current < instance.tools.Length)
                tool = instance.tools[current];
            if (tool != null && tool is MacroToolRunner)
                tool = null;
            return tool;
        }

        private void setPrefs(int id)
        {
            if (doNotSavePrefs)
                return;
            bool ok = isBuiltInTool(names[id]);
            string prefsName = instance.names[id];
            if (!ok)
            {
                string name = names[id];
                int i = name.IndexOf(" ("); // remove any hint in parens
                if (i > 0)
                {
                    name = name.Substring(0, i);
                    prefsName = name;
                }
            }

            int index = id - CUSTOM1;
            string key = TOOL_KEY + (index / 10) % 10 + index % 10;
            Prefs.set(key, prefsName);
        }

        private bool isBuiltInTool(String name)
        {
            for (int i = 0; i < builtInTools2.Length; i++)
            {
                if (name.Equals(builtInTools2[i]))
                    return true;
            }

            for (int i = 0; i < builtInTools.Length; i++)
            {
                if (name.StartsWith(builtInTools[i]))
                    return true;
            }

            return false;
        }

        /** Returns 'true' if the multi-point tool is enabled. */
        public static bool getMultiPointMode()
        {
            return multiPointMode;
        }
    }
}